/*
 * Copyright 2011 The Rusted Roof, LLC.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package org.proteusframework.platformservice.persistence.api;

import org.proteusframework.core.api.IDelegateConsumer;
import org.proteusframework.core.api.model.INamespace;
import org.proteusframework.platformservice.grammar.IGrammarConsumer;
import org.proteusframework.platformservice.grammar.IllegalExpressionException;
import org.proteusframework.platformservice.persistence.api.messagebean.IMessageBeanDescriptor;
import org.proteusframework.platformservice.persistence.api.messagebean.IMessageBeanDescriptorVisitor;
import org.proteusframework.platformservice.persistence.api.messagebean.IMessageBeanProperty;
import org.proteusframework.platformservice.persistence.messagebean.DataType;
import org.proteusframework.platformservice.persistence.messagebean.UnsupportedMessageBeanException;

import java.io.IOException;
import java.util.List;
import java.util.SortedSet;

/**
 * Message bean management routines. A message bean is typically a POJO that has been mapped into the persistence
 * service via an {@link IMessageBeanDescriptor}.
 *
 * @author Tacoma Four
 */
public interface IMessageBeanManager extends IDelegateConsumer, IGrammarConsumer
{
    String KEY_JDBC_DRIVER = "jdbc.driver";

    String KEY_GRAMMAR_INSPECTOR_FACTORY = "grammar.insepctor.factory";

    //
    // Message Persistence (Database)
    //

    /**
     * Flag that indicates if the persistence service supports a message store.
     *
     * @return true, if the persistence service supports a message store
     */
    boolean supportsMessageBeanManagement();

    /**
     * Set of registered {@link org.proteusframework.platformservice.persistence.api.messagebean.IMessageBeanDescriptor}s.
     *
     * @return Non-null set of message bean descriptors registered with this messageBeanManager
     */
    SortedSet<IMessageBeanDescriptor> getMessageBeanDescriptors();

    /**
     * Queue up a message bean for registration with the backing persistence store.
     *
     * @param messageBeanDescriptor Message bean descriptor
     * @throws UnsupportedMessageBeanException
     *          when the persistence store cannot handle the message bean
     */
    void queueMessageBeanRegistration(IMessageBeanDescriptor messageBeanDescriptor) throws UnsupportedMessageBeanException;

    /**
     * Process each message bean registration that has been queued up since the last execution.
     */
    void processRegistrationQueue();

    /**
     * Determine if the given message bean is already registered with the persistence service implementation.
     *
     * @param messageBeanDescriptor Message bean class
     * @return true, if already registered
     */
    boolean isMessageBeanRegistered(IMessageBeanDescriptor messageBeanDescriptor);

    /**
     * Determine if the given namespace is already registered as a message bean with the persistence service
     * implementation.
     *
     * @param namespace Namespace
     * @return true, if already registered
     */
    boolean isMessageBeanRegistered(INamespace namespace);

    /**
     * Locate the message bean descriptor that provides metadata that fully describes the message bean.
     *
     * @param namespace Namespace
     * @return Message bean descriptor, or null if no match was found for the provided namespace
     */
    IMessageBeanDescriptor getMessageBeanDescriptor(INamespace namespace);

    /**
     * Saves the specified message to the persistence store
     *
     * @param messageBeanClass Message class of the message
     * @param message          Message to persist
     * @param <T>              Specific message bean class type
     * @return identity value assigned by the database, if and only if the messageBeanClass included a mapped
     *         {@link DataType#IdentityType} property, otherwise a value of -1 is returned
     * @throws org.proteusframework.platformservice.persistence.messagebean.UnrecognizedMessageBeanException
     *          when the message was not registered with the persistence service
     */
    <T> long saveMessage(Class<T> messageBeanClass, T message);

    /**
     * Deletes the specified message from the persistence store.
     *
     * @param messageBeanClass Message class of the message
     * @param message          Message to delete
     * @param <T>              Specific message bean class type
     * @return true, if the message was successfully deleted
     * @throws org.proteusframework.platformservice.persistence.messagebean.UnrecognizedMessageBeanException
     *          when the message was not registered with the persistence service
     */
    <T> boolean deleteMessage(Class<T> messageBeanClass, T message);

    /**
     * Updates the specified message in the persistence store.
     *
     * @param messageBeanClass Message class of the message
     * @param message          Message to update; the primary key mapping columns must all have values assigned to them
     * @param properties       Which properties to include in the update to the existing record in the underlying
     *                         message store
     * @return true, if the message was successfully updated
     * @throws org.proteusframework.platformservice.persistence.messagebean.UnrecognizedMessageBeanException
     *          when the message was not registered with the persistence service
     */
    <T> boolean updateMessage(Class<T> messageBeanClass, T message, IMessageBeanProperty[] properties);

    /**
     * Lists all persisted messages for a specific message class. Be very careful executing this method if there is the
     * possibility of a large list!
     *
     * @param messageBeanClass Message class to query
     * @param <T>              Specific message bean class type
     * @return list of messages associated with the given message class
     * @throws org.proteusframework.platformservice.persistence.messagebean.UnrecognizedMessageBeanException
     *          when the message was not registered with the persistence service
     */
    <T> List<T> listMessages(Class<T> messageBeanClass);

    /**
     * Opens an iterator for traversing all persisted messages for a specific message class. If you open an iterator, you
     * <b>must</b> close the iterator!
     *
     * @param messageBeanClass Message class to query
     * @param <T>              Specific message bean class type
     * @return Iterator
     */
    <T> Iterable<T> createIterator(Class<T> messageBeanClass);

    /**
     * Executes a query against a specific message class.
     *
     * @param messageBeanClass Message class to query
     * @param query            SQL 92 WHERE clause. Specific persistence services will document how much of the standard
     *                         they support
     * @return Non-null result set wrapper
     * @throws org.proteusframework.platformservice.persistence.messagebean.UnrecognizedMessageBeanException
     *                                    when the message was not registered with the persistence service
     * @throws IllegalExpressionException when the filter represented by the query fails to pass the
     *                                    {@link org.proteusframework.platformservice.grammar.IGrammarInspector#inspect(String)}
     *                                    routine
     */
    <T> IResultSet<T> executeQuery(Class<T> messageBeanClass, String query) throws IllegalExpressionException;

    /**
     * Executes a query against a specific message class using parameter substitution.
     *
     * @param messageBeanClass Message class to query
     * @param query            SQL 92 WHERE clause. Specific persistence services will document how much of the standard
     *                         they support
     * @param params           Substitution parameters
     * @return Result set
     * @throws org.proteusframework.platformservice.persistence.messagebean.UnrecognizedMessageBeanException
     *                                    when the message was not registered with the persistence service
     * @throws IllegalExpressionException when the filter represented by the query fails to pass the
     *                                    {@link org.proteusframework.platformservice.grammar.IGrammarInspector#inspect(String)}
     *                                    routine
     */
    <T> IResultSet<T> executeQuery(Class<T> messageBeanClass, String query, Object... params)
            throws IllegalExpressionException;

    /**
     * Flag that indicates if the concrete persistence service implementation supports parameterized queries.
     *
     * @return true, if the concrete implementation supports parameterized queries
     */
    boolean supportsParameterizedQueries();

    /**
     * Flag that indicates if the concrete persistence service implementation supports a subset of a SQL-92 expression
     * syntax for narrowing the scope of a query.
     *
     * @return true, if the concrete implementation supports expression-based queries
     */
    boolean supportsExpressionQueries();

    /**
     * Flag that indicates if the concrete persistence service implementation supports message bean updates, or
     * if a message bean persisted in the underlying message store via {@link #saveMessage(Class, Object)} is
     * considered immutable.
     *
     * @return true, if the concrete implementation supports message bean updates
     */
    boolean supportsUpdates();

    /**
     * Flag that indicates if the concrete persistence service implementation supports message bean deletion, or
     * if a message bean persisted in the underlying message store via {@link #saveMessage(Class, Object)} is
     * considered immutable.
     *
     * @return true, if the concrete implementation supports message bean deletion
     */
    boolean supportsDeletes();

    /**
     * Returns a list of expressions supported by the persistence service.
     *
     * @return Non-null list of expression tokens supported by the persistence service. This list will be empty when
     *         {@link #supportsExpressionQueries()} is false
     */
    List<String> describeExpressionSupport();

    /**
     * Returns a list of {@link DataType}s supported by the persistence service.
     *
     * @return Non-null list of DataTypes supported by the persistence service. This list will be empty when
     *         {@link #supportsMessageBeanManagement()} is false
     */
    List<DataType> describeDataTypeSupport();

    /**
     * Flag that indicates if the persistence service can support message bean index creation.
     *
     * @return true, if the persistence service can support index creation
     */
    boolean supportsIndices();

    /**
     * Flag that indicates if the persistence service can support indirect message bean references via an interface.
     *
     * @return true, if the persistence service can support message operations using an interface type instead of a
     *         concrete type.
     */
    boolean supportsInterfaces();

    /**
     * Accepts a message bean descriptor serializer visitor. The manager must iterate across each registered message
     * bean descriptor, offering each instance up to the serialization visitor.
     *
     * @param visitor Serialization visitor
     * @throws java.io.IOException When the packaging operation cannot complete
     */
    void accept(IMessageBeanDescriptorVisitor visitor) throws IOException;
}
